package org.example.lesson;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

/**
 * 基于TCP协议的回显服务器
 */

public class TCPEchoServer {
    //声明用于服务器端的类
    protected ServerSocket socket;

    /**
     * 构造方法
     * @param port 服务端的端口号
     */
    public TCPEchoServer(int port) throws IOException {
        //校验端口号
        if(port<1024||port>65535){
            throw new BindException("端口号必须在1024和65535之间.");
        }
        //根据指定的端口号初始化Socket服务
        this.socket=new ServerSocket(port);
    }

    /**
     * 开始处理客户端的连接请求
     */
    public void start() throws IOException {
        System.out.println("服务已启动，等待客户端连接.");
        //循环处理连接请求
        while(true){
            //接收客户端请求
            Socket clientSocket = socket.accept();
            //接收到客户端连接之后，交给专门的方法进行数据处理
            processConnections(clientSocket);
        }
    }
    //为了方便维护，专门把数据处理的逻辑抽取成一个方法
    protected void processConnections(Socket clientSocket) {
        //打印连接日志
        System.out.printf("[%s:%d] 客户端上线了.\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
        //所有的通信数据都封装在Socket对象中，对应的就是入参clientSocket，TCP传输形式时字节流
        //1.通过输入输出流来获取和发送数据
        try(InputStream inputStream= clientSocket.getInputStream();
            OutputStream outputStream= clientSocket.getOutputStream()){
            //2.循环读取客户端发来的数据
            while(true){
                //通过Scanner简化文本请求的读取过程
                Scanner in=new Scanner(inputStream);
                //3.判断是否还有数据
                if(!in.hasNextLine()){
                    System.out.printf("[%s:%d] 客户端下线了.\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
                    break;
                }
                //4.获取用户发来的具体内容
                String request=in.nextLine();
                //5.根据请求来计算响应
                String response=process(request);
                //6.把响应写入输出流
                //6.1 通过PrintWriter来简化写入操作
                PrintWriter writer = new PrintWriter(outputStream);
                //6.2 向输出流中写入响应
                writer.println(response);
                //7.强制刷新缓冲区，相当于系统把数据交给了网卡，也相当于程序把数据发送出去了
                writer.flush();
                //8.打印日志
                System.out.printf("[%s:%d] = %s, response= %s\n",clientSocket.getInetAddress().toString(),clientSocket.getPort(),request,response);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    //处理响应的方法
    protected String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TCPEchoServer server=new TCPEchoServer(9090);
        server.start();
    }
}
